/*
 * 创建日期 2005-5-24
 *
 * TODO 要更改此生成的文件的模板，请转至
 * 窗口 － 首选项 － Java － 代码样式 － 代码模板
 */
package com.ctvit.framework.excel;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.Date;
import java.util.List;

import jxl.Cell;
import jxl.CellType;
import jxl.Range;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.write.Blank;
import jxl.write.Boolean;
import jxl.write.DateTime;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCell;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

import com.ctvit.framework.web.context.ContextHolder;

/**
 * Excel模板实现.
 * 
 * TODO: 修改JXL实现为POI实现.
 * 
 * @author WangChen
 */
public class ExcelTemplate {

	public static void output(String templateFileName, PrintContext context, OutputStream os) throws Exception {
		output(templateFileName, context, os, null);
	}

	public static void output(String templateFileName, PrintContext context, OutputStream os,
			ExcelExportListener listener) throws Exception {
		// String file =
		// ExcelTemplate.class.getResource(templateFileName).getFile();
		String file = ContextHolder.getWebRoot() + "/export/template/" + templateFileName;
		output(new FileInputStream(file), context, os, listener);
	}

	public static void output(InputStream templateStream, PrintContext context, OutputStream os,
			ExcelExportListener listener) throws Exception {
		// 构建Workbook对象, 只读Workbook对象
		// 直接从本地文件创建Workbook
		// 从输入流创建Workbook
		WritableWorkbook owb = null;
		WorkbookSettings setting1 = new WorkbookSettings();
		setting1.setInitialFileSize(50000);
		setting1.setArrayGrowSize(50000);
		setting1.setGCDisabled(true);

		Workbook rwb = Workbook.getWorkbook(templateStream, setting1);
		Sheet sourceSheet = rwb.getSheet(0);
		try {
			WorkbookSettings setting2 = new WorkbookSettings();
			setting2.setInitialFileSize(100000);
			setting2.setArrayGrowSize(50000);
			setting2.setGCDisabled(true);

			owb = Workbook.createWorkbook(os, rwb, setting2);
			//
			int pages = context.getPageCount();

			if (pages > 1) {
				for (int i = 1; i < pages; i++) {
					owb.copySheet(0, "Sheet" + i, i);
					copySheetSetting(owb.getSheet(i), sourceSheet);
				}
			}
			for (int i = 0; i < pages; i++) {
				if (listener != null) {
					context.currentPage = i;
					SheetInitEvent event = new SheetInitEvent(context);
					listener.handleSheetInitEvent(event);
				}
				owb.getSheet(i).setName(context.getSheetLabel());
				WritableSheet destSheet = owb.getSheet(i);
				fillPage(destSheet, 0, sourceSheet.getRows() - 1, context);
			}
			owb.write();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			try {
				if (owb != null)
					owb.close();
				if (rwb != null)
					rwb.close();
				if (templateStream != null)
					templateStream.close();
			} catch (Exception e) {
			}
		}
	}

	private static void copySheetSetting(WritableSheet destsheet, Sheet sourcesheet) {
		destsheet.getSettings().setCopies(sourcesheet.getSettings().getCopies());
		destsheet.getSettings().setBottomMargin(sourcesheet.getSettings().getBottomMargin());
		destsheet.getSettings().setDefaultColumnWidth(sourcesheet.getSettings().getDefaultColumnWidth());
		destsheet.getSettings().setDefaultRowHeight(sourcesheet.getSettings().getDefaultRowHeight());
		destsheet.getSettings().setDisplayZeroValues(sourcesheet.getSettings().getDisplayZeroValues());
		destsheet.getSettings().setFitHeight(sourcesheet.getSettings().getFitHeight());
		destsheet.getSettings().setFitToPages(sourcesheet.getSettings().getFitToPages());
		destsheet.getSettings().setFitWidth(sourcesheet.getSettings().getFitWidth());
		destsheet.getSettings().setFooter(sourcesheet.getSettings().getFooter());
		destsheet.getSettings().setFooterMargin(sourcesheet.getSettings().getFooterMargin());
		destsheet.getSettings().setHeader(sourcesheet.getSettings().getHeader());
		destsheet.getSettings().setHeaderMargin(sourcesheet.getSettings().getHeaderMargin());
		destsheet.getSettings().setHorizontalFreeze(sourcesheet.getSettings().getHorizontalFreeze());
		destsheet.getSettings().setHorizontalPrintResolution(sourcesheet.getSettings().getHorizontalPrintResolution());
		destsheet.getSettings().setLeftMargin(sourcesheet.getSettings().getLeftMargin());
		destsheet.getSettings().setOrientation(sourcesheet.getSettings().getOrientation());
		destsheet.getSettings().setPageStart(sourcesheet.getSettings().getPageStart());
		destsheet.getSettings().setPassword(sourcesheet.getSettings().getPassword());
		destsheet.getSettings().setPasswordHash(sourcesheet.getSettings().getPasswordHash());
		destsheet.getSettings().setPrintGridLines(sourcesheet.getSettings().getPrintGridLines());
		destsheet.getSettings().setPrintHeaders(sourcesheet.getSettings().getPrintHeaders());
		destsheet.getSettings().setRightMargin(sourcesheet.getSettings().getRightMargin());
		destsheet.getSettings().setScaleFactor(sourcesheet.getSettings().getScaleFactor());
		destsheet.getSettings().setShowGridLines(sourcesheet.getSettings().getShowGridLines());
		destsheet.getSettings().setTopMargin(sourcesheet.getSettings().getTopMargin());
		destsheet.getSettings().setVerticalFreeze(sourcesheet.getSettings().getVerticalFreeze());
		destsheet.getSettings().setVerticalPrintResolution(sourcesheet.getSettings().getVerticalPrintResolution());
		destsheet.getSettings().setZoomFactor(sourcesheet.getSettings().getZoomFactor());
	}

	private static void fillPage(WritableSheet sheet, int start, int end, PrintContext context) throws Exception {
		int row = 0;
		int col = 0;
		try {
			for (row = end; row >= start; row--) {
				int maxrows = 1;
				for (col = 0; col < sheet.getColumns(); col++) {
					WritableCell cell = sheet.getWritableCell(col, row);
					maxrows = fillCell(sheet, cell, context, maxrows);
				}
				for (int i = 1; i < maxrows; i++) {
					sheet.setRowView(row + i, sheet.getRowView(row).getSize());
				}
			}
		} catch (Exception e) {
			throw new Exception("Row:" + row + ",Col:" + col, e);
		}
	}

	private static int fillCell(WritableSheet sheet, WritableCell cell, PrintContext context, int maxRows)
			throws Exception {
		if (cell.getCellFeatures() != null && cell.getCellFeatures().getComment() != null) {
			String expression = cell.getCellFeatures().getComment();
			String cellExpression=cell.getContents();
			if (cellExpression==null) cellExpression="";
			Object newValue = context.getValue(expression);
			Object myCellVal=null;
			cell.getCellFeatures().removeComment();
			if (newValue == null) {
				fillCellContent(sheet, cell, cell.getRow(), cell.getColumn(), null);
			} else if (!(newValue instanceof List) && !(newValue.getClass().isArray())) {
				fillCellContent(sheet, cell, cell.getRow(), cell.getColumn(), newValue);
			} else {
				int size = getSize(newValue);
				if (size == 0) {
					fillCellContent(sheet, cell, cell.getRow(), cell.getColumn(), null);
				} else {
					for (int i = 0; i < size; i++) {
						WritableCell aCell;
						if (i == 0) {
							aCell = cell;
						} else {
							if (i >= maxRows) {
								// insertRow(sheet,sheet,cell.getRow(),cell.getRow()+i,false);
								sheet.insertRow(cell.getRow() + i);
								maxRows++;
							}
							aCell = sheet.getWritableCell(cell.getColumn(), cell.getRow() + i);
						}
						myCellVal=getValue(newValue, i);
						if (!cellExpression.equals("")){
							context.addAttribute("myCellVal", myCellVal);
							myCellVal=context.getValue(cellExpression);
						}
						fillCellContent(sheet, cell, aCell.getRow(), aCell.getColumn(),myCellVal);
						if (i > 0) {
							copyMergeInfo(sheet, sheet, cell.getRow(), cell.getRow() + i, cell.getColumn(), cell
									.getColumn());
						}

					}
				}
			}
		}
		return maxRows;
	}

	private static int getSize(Object values) {
		if (values instanceof List) {
			return ((List) values).size();
		}
		if (values.getClass().isArray()) {
			return Array.getLength(values);
		}
		return 0;
	}

	private static Object getValue(Object values, int index) {
		if (values instanceof List) {
			return ((List) values).get(index);
		}
		if (values.getClass().isArray()) {
			return Array.get(values, index);
		}
		return null;

	}

	private static void copyMergeInfo(Sheet source, WritableSheet dest, int sourceRow, int destRow, int sourceColumn,
			int destColumn) throws Exception {
		for (int column = sourceColumn; column <= destColumn; column++) {
			Range range = getRange(source, sourceRow, column);
			if (range != null && sourceRow == range.getTopLeft().getRow() && column == range.getTopLeft().getColumn()) {
				setCellFormat(dest, column, destRow, range.getBottomRight().getColumn(), destRow);
				dest.mergeCells(column, destRow, range.getBottomRight().getColumn(), destRow);
			}
		}
	}

	private static void setCellFormat(WritableSheet sheet, int left, int top, int right, int bottom) throws Exception {
		for (int j = left; j <= right; j++) {
			Cell firstCell = sheet.getCell(left, top);
			if (firstCell != null) {
				jxl.format.CellFormat format = firstCell.getCellFormat();
				for (int i = top; i <= bottom; i++) {
					if (i != top || j != left) {
						sheet.addCell(new Blank(j, i, format));
					}
				}
			}
		}
	}

	private static Range getRange(Sheet sheet, int row, int col) {
		Range[] ranges = sheet.getMergedCells();
		for (int i = 0; i < ranges.length; i++) {
			if (row >= ranges[i].getTopLeft().getRow() && col >= ranges[i].getTopLeft().getColumn()
					&& row <= ranges[i].getBottomRight().getRow() && col <= ranges[i].getBottomRight().getColumn()) {
				return ranges[i];
			}
		}
		return null;
	}

	private static void fillCellContent(WritableSheet sheet, WritableCell cell, int row, int col, Object newValue)
			throws Exception {
		if (newValue != null) {
			if (cell.getType() == CellType.LABEL) {
				sheet.addCell(new Label(col, row, newValue.toString(), cell.getCellFormat()));
			} else if (cell.getType() == CellType.NUMBER) {
				sheet.addCell(new Number(col, row, ((java.lang.Number) newValue).doubleValue(), cell.getCellFormat()));
			} else if (cell.getType() == CellType.BOOLEAN) {
				sheet
						.addCell(new Boolean(col, row, ((java.lang.Boolean) newValue).booleanValue(), cell
								.getCellFormat()));
			} else if (cell.getType() == CellType.DATE) {
				sheet.addCell(new DateTime(col, row, (Date) newValue, cell.getCellFormat()));
			} else if (cell.getType() == CellType.EMPTY) {
				sheet.addCell(new Label(col, row, newValue.toString(), cell.getCellFormat()));
			}
		} else {
			sheet.addCell(new Blank(col, row, cell.getCellFormat()));
		}
	}
}